package jellyfishumbrella.tikis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Random;

import megamu.mesh.Region;
import megamu.mesh.Site;

import com.jellyfishumbrella.GL.AbstractActivity;
import com.jellyfishumbrella.GL.UberPoly;
import com.jellyfishumbrella.GL.Vector3d;
import com.jellyfishumbrella.util.Trace;

/**
 * TODO Put here a description of what this class does.
 * 
 * @author 500441547. Created Sep 4, 2012.
 */
public class Volcano extends VorLand2D {
	private static boolean						SPLIT_FLOW				= false;
	private static final float					FLOW_THRESHHOLD			= 0.1f;
	private static final float					LAVA_HARDEN_RATIO		= 0.9f;
	public static final double					ERUPT_GROW_AMOUNT		= 1;									// amount the caldera region permanently grows with each eruption
	public static final double					HARDEN_ON_FLOW_AMOUNT	= 1;									// amount lava hardens before flowing off a region
	public static final double					LAVA_AMOUNT				= 90;
	public static final float					MIN_ALT					= 0;
	public static final float					MAX_ALT					= 1000;
	public static Random						rand					= new Random();

	public static LinkedHashMap<Site, Double>	raiseMoundList			= new LinkedHashMap<Site, Double>();
	public static LinkedHashSet<Site>			flowMoundList			= new LinkedHashSet<Site>();
	public static LinkedHashSet<Site>			hardenMoundList			= new LinkedHashSet<Site>();
	public static LinkedList<Region>			dirtyRegions			= new LinkedList<Region>();

	public Volcano() {
		super();
	}

	public Volcano(GameLogic logic) {
		super(logic);
	}

	public void makeRegion(Site site) {
		for (int i = -COASTBORDER; i <= COASTBORDER; i++) {
			for (int j = -COASTBORDER; j <= COASTBORDER; j++) {
				int i1 = site.iIndex + i;
				int j1 = site.jIndex + j;
				Site borderSite = sites[i1][j1];
				if (borderSite.region == null) {
					voronoi2.addSite(sites[i1][j1]);
				}
			}
		}
		ArrayList<Region> regions = voronoi2.getRegions(sites);
		sendPolygonsToGL(regions);
	}

	public void changeTestSurface(Site site) {
		for (int i = -COASTBORDER; i <= COASTBORDER; i++) {
			for (int j = -COASTBORDER; j <= COASTBORDER; j++) {
				int i1 = site.iIndex + i;
				int j1 = site.jIndex + j;
				Site borderSite = sites[i1][j1];
				if (borderSite.region != null) {
					borderSite.region.setSurface("beach");
				}
			}
		}
		// ArrayList<Region> regions = voronoi2.getRegions(sites);
		// sendPolygonsToGL(regions);
	}

	public void processMoundLists() {
		boolean reProcess = false;

		while (hardenMoundList.size() > 0) {
			Site site = (Site) hardenMoundList.toArray()[0];
			hardenMoundList.remove(site);

			// GameLogic.activityQ.add(GameLogic.volcanoIsland.new HardenMoundActivity(md.region));
			hardenMound(site);

			dirtyRegions.add(site.region);

			reProcess = true;
		}

		// refreshMesh();

		while (flowMoundList.size() > 0) {
			Site site = (Site) flowMoundList.toArray()[0];
			flowMoundList.remove(site);

			// GameLogic.activityQ.add(GameLogic.volcanoIsland.new RaiseMoundActivity(md.region, md.amount));
			flowMound(site);

			dirtyRegions.add(site.region);

			reProcess = true;
		}

		// refreshMesh();

		Collection<Site> raiseSites = raiseMoundList.keySet();
		Iterator<Site> itr = raiseSites.iterator();
		while (itr.hasNext()) {
			Site site = itr.next();
			double amount = raiseMoundList.get(site);

			// GameLogic.activityQ.add(GameLogic.volcanoIsland.new FlowMoundActivity(md.region));
			raiseMound(site, amount);

			dirtyRegions.add(site.region);

			reProcess = true;

			itr.remove();
		}

		refreshMesh();

		if (reProcess) {
			GameLogic.activityQ.add(GameLogic.volcanoIsland.new ProcessMoundListActivity());
		} else {
			// GameLogic.activityQ.add(GameLogic.volcanoIsland.new EruptActivity(GameLogic.volcanoIsland.center, Volcano.LAVA_AMOUNT));
		}
	}

	public void refreshMesh() {
		while (!dirtyRegions.isEmpty()) {
			Region currRegion = dirtyRegions.pop();
			currRegion.refreshMesh();
		}
	}

	public void erupt(Site site, double amount) {
		synchronized (site) {
			Vector3d translation = new Vector3d(0, ERUPT_GROW_AMOUNT, 0);
			makeRegion(site);
			site.vert.add(translation);

			// region.setMoundHeight(0);

			// GameLogic.activityQ.add(GameLogic.volcanoIsland.new RaiseMoundActivity(region, amount));
			raiseMoundList.put(site, amount);
		}
		GameLogic.activityQ.add(GameLogic.volcanoIsland.new ProcessMoundListActivity());
	}

	public synchronized void raiseMound(Site site, double amount) {
		synchronized (site) {
			Trace.getCurrentMethodName("MoundHeight = " + site.region.getMoundHeight() + "  amount: " + amount);
			Vector3d translation = new Vector3d(0, amount, 0);
			site.region.translateVertexByType(translation, UberPoly.VertexLODTag.CENTER, 1.0, false);
			site.region.translateVertexByType(translation, UberPoly.VertexLODTag.INNER, 1.0, false);

			site.region.setMoundHeight(site.region.getMoundHeight() + amount);

			if (site.vert.y() <= 0) {
				// GameLogic.activityQ.add(0, GameLogic.volcanoIsland.new HardenMoundActivity(region));
// hardenMoundList.add(site);
			} else {
				// GameLogic.activityQ.add(GameLogic.volcanoIsland.new FlowMoundActivity(region));
// flowMoundList.add(site);
			}
		}
	}

	public synchronized void hardenMound(Site site) {
		synchronized (site) {
			double amount = site.region.getMoundHeight();
			Trace.getCurrentMethodName("amount: " + amount);

			Vector3d translationSite = new Vector3d(0, amount, 0);

			site.vert.add(translationSite);
			site.region.setMoundHeight(0);

			// region.translateVertexByType(translationSite, UberPoly.VertexLODTag.CENTER, 1.0, false);
			// region.translateVertexByType(translationSite, UberPoly.VertexLODTag.INNER, 1.0, false);
			site.region.translateVertexByType(translationSite, UberPoly.VertexLODTag.OUTER, 1.0, false);
// region.smoothOUTERpoints(false);
		}
	}

	public synchronized void flowMound(Site site) {
		synchronized (site) {
			Vector3d translation = new Vector3d(0, HARDEN_ON_FLOW_AMOUNT, 0);
			site.vert.add(translation);
			site.region.setMoundHeight(site.region.getMoundHeight() - HARDEN_ON_FLOW_AMOUNT);

			double amount = site.region.getMoundHeight() - HARDEN_ON_FLOW_AMOUNT;
			Trace.getCurrentMethodName("flow amount = " + amount);
			Iterator itr = site.neighbors.iterator();

			LinkedHashSet<Site> flowToSites = new LinkedHashSet<Site>();

			double minSlope = 999999;
			// double maxSlope = -999999;
			Site minNeighbor = null;

			while (itr.hasNext()) {
				Site currNeighbor = (Site) itr.next();

				double currSlope = (site.vert.y() + currNeighbor.region.getMoundHeight()) - (site.vert.y() + site.region.getMoundHeight());
				minSlope = Math.min(minSlope, currSlope);
				if (currSlope <= 0 && currSlope <= minSlope) {
					if (SPLIT_FLOW) {
						flowToSites.add(currNeighbor);
					} else {
						minNeighbor = currNeighbor;
					}
				}
			}

			if (!SPLIT_FLOW) {
				flowToSites.add(minNeighbor);
			}

			int numSites = flowToSites.size();

			if (numSites > 0) {
				double newAmount = (amount) / (numSites + 1);
				if (newAmount >= FLOW_THRESHHOLD) {
					raiseMoundList.put(site, -newAmount * flowToSites.size());
					for (Site currNeighbor : flowToSites) {
						raiseMoundList.put(currNeighbor, newAmount);
						// raiseMoundList.put(region, -newAmount);
					}
				} else {
					Trace.getCurrentMethodName("newAmount = " + newAmount);
					// hardenMoundList.add(new MoundData(region, 0));
				}
			} else {
				Trace.getCurrentMethodName("numRegions = " + numSites);
				// hardenMoundList.add(new MoundData(region, 0));
			}
		}
	}

	public class ProcessMoundListActivity extends AbstractActivity {

		public ProcessMoundListActivity() {
		}

		@Override
		public void process() {
			Trace.getCurrentMethodName("ProcessMoundList activity");
			processMoundLists();
		}
	}

	public class EruptActivity extends AbstractActivity {
		Site	eruptSite	= null;
		double	amount		= 0;

		public EruptActivity(Site eruptSite, double amount) {
			this.eruptSite = eruptSite;
			this.amount = amount;
		}

		@Override
		public void process() {
			Trace.getCurrentMethodName("erupt activity");
			erupt(eruptSite, amount);
		}
	}

	public class TranslateActivity extends AbstractActivity {
		Site		currSite	= null;
		Vector3d	translation;

		public TranslateActivity(Site currSite, Vector3d translation) {
			this.currSite = currSite;
			this.translation = translation;
		}

		@Override
		public void process() {
			Trace.getCurrentMethodName("translation activity");

			currSite.region.translate(translation, true);
			// adjustSurfaceByAltitude(currRegion);
		}
	}

	public class RaiseMoundActivity extends AbstractActivity {
		Site	currSite	= null;
		double	amount;

		public RaiseMoundActivity(Site currSite, double amount) {
			this.currSite = currSite;
			this.amount = amount;
		}

		@Override
		public void process() {
			Trace.getCurrentMethodName("raiseMound activity");
			raiseMound(currSite, amount);
		}
	}

	public class FlowMoundActivity extends AbstractActivity {
		Site	currSite	= null;

		public FlowMoundActivity(Site currSite) {
			this.currSite = currSite;
		}

		@Override
		public void process() {
			Trace.getCurrentMethodName("flowMound activity");
			flowMound(currSite);
		}
	}

	public class HardenMoundActivity extends AbstractActivity {
		Site	currSite	= null;

		public HardenMoundActivity(Site currSite) {
			this.currSite = currSite;
		}

		@Override
		public void process() {
			Trace.getCurrentMethodName("hardenMound activity");
			hardenMound(currSite);
		}
	}
}
